//----------------------------------------------------------------
//module name : yhz_cpu_diff
//engineer : yhz
//date : 2021.10.01
//----------------------------------------------------------------
`include "yhz_defines.v"
module yhz_cpu_diff(
    input  wire        i_clk               ,
    input  wire        i_rst               ,
    //IF
    input  wire [63:0] i_r_inst_data       ,
    input  wire        i_r_inst_ready      ,
    output wire        o_r_inst_valid      ,
    output wire [63:0] o_r_inst_addr       ,
    //RAM
    input  wire        i_w_ram_ready       ,
    output wire        o_w_ram_valid       ,
    output wire [63:0] o_w_ram_addr        ,
    output wire [63:0] o_w_ram_data        ,
    output wire [63:0] o_w_ram_mask        ,
    input  wire        i_r_ram_ready       ,
    output wire        o_r_ram_valid       ,
    output wire [63:0] o_r_ram_addr        ,
    input  wire [63:0] i_r_ram_data        ,
    //difftest
    output wire        diff_uart_out_valid ,
    output wire [7:0]  diff_uart_out_ch     
);
//----------------------------------------------------------------
//register & wire
//----------------------------------------------------------------
    wire [63:0] diff_pc_addr             ;
    wire [31:0] diff_instruction         ;
    wire        diff_pipeline_pulse      ;
    wire        diff_clint_mem           ;
    wire        diff_w_rd_en             ;
    wire [4:0]  diff_w_rd_addr           ;
    wire [63:0] diff_w_rd_data           ;
    wire        diff_trap_ecall          ;
    wire        diff_trap_timer          ;

    wire [63:0] diff_mstatus             ;
    wire [63:0] diff_mepc                ;
    wire [63:0] diff_mtvec               ;
    wire [63:0] diff_mcause              ;
    wire [63:0] diff_mip                 ;
    wire [63:0] diff_mie                 ;
    wire [63:0] diff_mscratch            ;
    wire [63:0] diff_mcycle              ;
    wire [63:0] diff_reg_difftest [31:0] ;
//----------------------------------------------------------------
//module
//----------------------------------------------------------------
    yhz_riscv64i cpu(
        .i_clk               (i_clk              ),
        .i_rst               (i_rst              ),
        //IF
        .i_r_inst_data       (i_r_inst_data      ),
        .i_r_inst_ready      (i_r_inst_ready     ),
        .o_r_inst_valid      (o_r_inst_valid     ),
        .o_r_inst_addr       (o_r_inst_addr      ),
        //RAM
        .i_w_ram_ready       (i_w_ram_ready      ),
        .o_w_ram_valid       (o_w_ram_valid      ),
        .o_w_ram_addr        (o_w_ram_addr       ),
        .o_w_ram_data        (o_w_ram_data       ),
        .o_w_ram_mask        (o_w_ram_mask       ),
        .i_r_ram_ready       (i_r_ram_ready      ),
        .o_r_ram_valid       (o_r_ram_valid      ),
        .o_r_ram_addr        (o_r_ram_addr       ),
        .i_r_ram_data        (i_r_ram_data       ),
        //difftest
        .diff_uart_out_valid (diff_uart_out_valid),
        .diff_uart_out_ch    (diff_uart_out_ch   ),

        .diff_pc_addr        (diff_pc_addr       ),
        .diff_instruction    (diff_instruction   ),
        .diff_pipeline_pulse (diff_pipeline_pulse),
        .diff_clint_mem      (diff_clint_mem     ),
        .diff_w_rd_en        (diff_w_rd_en       ),
        .diff_w_rd_addr      (diff_w_rd_addr     ),
        .diff_w_rd_data      (diff_w_rd_data     ),
        .diff_trap_ecall     (diff_trap_ecall    ),
        .diff_trap_timer     (diff_trap_timer    ),

        .diff_mstatus        (diff_mstatus       ),
        .diff_mepc           (diff_mepc          ),
        .diff_mtvec          (diff_mtvec         ),
        .diff_mcause         (diff_mcause        ),
        .diff_mip            (diff_mip           ),
        .diff_mie            (diff_mie           ),
        .diff_mscratch       (diff_mscratch      ),
        .diff_reg_difftest   (diff_reg_difftest  ) 
    );
//----------------------------------------------------------------
//difftest
//----------------------------------------------------------------
    wire skip_flag = (diff_instruction == 32'h0000007b) || //uart
                     ((diff_instruction[31:20] == 12'hb00)&&(diff_instruction[6:0] == 7'b1110011)) ;//ecall
    wire trap_flag = (diff_instruction == 32'h0000006b)? 1'b1 : 1'b0 ;
    
    reg [5:0]   valid            ;
    reg [319:0] pc               ;
    reg [159:0] instr            ;
    reg [3:0]   clint_mem        ;
    reg [4:0]   skip             ;
    reg         wen              ;
    reg [4:0]   wdest            ;
    reg [63:0]  wdata            ;
    reg         wen_t            ;
    reg [4:0]   wdest_t          ;
    reg [63:0]  wdata_t          ;
    reg [63:0]  regs_diff [31:0] ;
    reg [4:0]   trap_valid       ;
    reg [7:0]   trap_code        ;
    reg [63:0]  cycleCnt         ;
    reg [63:0]  instrCnt         ;
    reg [4:0]   trap_ecall       ;
    reg [4:0]   trap_timer       ;
    reg [255:0] mstatus          ;
    reg [255:0] mepc             ;
    reg [255:0] mtvec            ;
    reg [255:0] mcause           ;
    reg [255:0] mip              ;
    reg [255:0] mie              ;
    reg [255:0] mscratch         ;
    
    always @(posedge i_clk) begin
        if(i_rst) begin
            valid      <= 6'd0   ;
            pc         <= 320'd0 ;
            instr      <= 160'd0 ;
            clint_mem  <= 4'd0   ;
            skip       <= 5'd0   ;
            wen        <= 1'b0   ;
            wdest      <= 5'd0   ;
            wdata      <= 64'd0  ;
            wen_t      <= 1'b0   ;
            wdest_t    <= 5'd0   ;
            wdata_t    <= 64'd0  ;
            trap_valid <= 5'b0   ;
            trap_code  <= 8'd0   ;
            cycleCnt   <= 64'd0  ;
            instrCnt   <= 64'd0  ;
            trap_ecall <= 5'd0   ;
            trap_timer <= 5'd0   ;
            mstatus    <= 256'd0 ;
            mepc       <= 256'd0 ;
            mtvec      <= 256'd0 ;
            mcause     <= 256'd0 ;
            mip        <= 256'd0 ;
            mie        <= 256'd0 ;
            mscratch   <= 256'd0 ;
        end
        else if(~trap_valid_t) begin
            if(diff_pipeline_pulse) begin
                valid      <= valid                                               ;
                pc         <= pc                                                  ;
                instr      <= instr                                               ;
                clint_mem  <= clint_mem                                           ;
                skip       <= skip                                                ;
                wen        <= wen                                                 ;
                wdest      <= wdest                                               ;
                wdata      <= wdata                                               ;
                wen_t      <= wen_t                                               ;
                wdest_t    <= wdest_t                                             ;
                wdata_t    <= wdata_t                                             ;
                regs_diff  <= regs_diff                                           ;
                trap_valid <= trap_valid                                          ;
                trap_code  <= trap_code                                           ;
                cycleCnt   <= cycleCnt + 1                                        ;
                instrCnt   <= instrCnt + (o_r_inst_valid & i_r_inst_ready)        ;
                trap_ecall <= trap_ecall                                          ;
                trap_timer <= trap_timer                                          ;
                mstatus    <= mstatus                                             ;
                mepc       <= mepc                                                ;
                mtvec      <= mtvec                                               ;
                mcause     <= mcause                                              ;
                mip        <= mip                                                 ;
                mie        <= mie                                                 ;
                mscratch   <= mscratch                                            ;
            end
            else begin
                valid      <= {valid     [4  :0],o_r_inst_valid & i_r_inst_ready} ;
                pc         <= {pc        [255:0],diff_pc_addr                   } ;
                instr      <= {instr     [127:0],diff_instruction               } ;
                clint_mem  <= {clint_mem [2  :0],diff_clint_mem                 } ;
                skip       <= {skip      [3  :0],skip_flag                      } ;
                wen        <= diff_w_rd_en                                        ;
                wdest      <= {3'b000,diff_w_rd_addr}                             ;
                wdata      <= diff_w_rd_data                                      ;
                wen_t      <= wen                                                 ;
                wdest_t    <= {3'b000,wdest}                                      ;
                wdata_t    <= wdata                                               ;
                regs_diff  <= diff_reg_difftest                                   ;
                trap_valid <= {trap_valid[3  :0],trap_flag                      } ;
                trap_code  <= diff_reg_difftest[10][7:0]                          ;
                cycleCnt   <= cycleCnt + 1                                        ;
                instrCnt   <= instrCnt + (o_r_inst_valid & i_r_inst_ready)        ;
                trap_ecall <= {trap_ecall[3  :0],diff_trap_ecall}                 ;
                trap_timer <= {trap_timer[3  :0],diff_trap_timer}                 ;
                mstatus    <= {mstatus   [191:0],diff_mstatus                   } ;
                mepc       <= {mepc      [191:0],diff_mepc                      } ;
                mtvec      <= {mtvec     [191:0],diff_mtvec                     } ;
                mcause     <= {mcause    [191:0],diff_mcause                    } ;
                mip        <= {mip       [191:0],diff_mip                       } ;
                mie        <= {mie       [191:0],diff_mie                       } ;
                mscratch   <= {mscratch  [191:0],diff_mscratch                  } ;
            end
        end
    end

    wire        valid_t      = (~trap_timer_t) & (~trap_ecall_t) & valid[4      ] ;
    wire [63:0] pc_t         = pc                                       [319:256] ;
    wire [31:0] instr_t      = instr                                    [159:128] ;
    wire        clint_mem_t  = clint_mem                                [3      ] ;
    wire        skip_t       = clint_mem_t | skip                       [4      ] ;
    DifftestInstrCommit DifftestInstrCommit(
        .clock    (i_clk                        ),
        .coreid   (0                            ),
        .index    (0                            ),
        .valid    (valid_t                      ),
        .pc       (pc_t                         ),
        .instr    (instr_t                      ),
        .skip     (skip_t                       ),
        .isRVC    (0                            ),
        .scFailed (0                            ),
        .wen      (clint_mem_t ? wen_t   : wen  ),
        .wdest    (clint_mem_t ? wdest_t : wdest),
        .wdata    (clint_mem_t ? wdata_t : wdata),
        .special  (0                            ) 
    );

    DifftestArchIntRegState DifftestArchIntRegState (
        .clock  (i_clk        ),
        .coreid (0            ),
        .gpr_0  (regs_diff[0] ),
        .gpr_1  (regs_diff[1] ),
        .gpr_2  (regs_diff[2] ),
        .gpr_3  (regs_diff[3] ),
        .gpr_4  (regs_diff[4] ),
        .gpr_5  (regs_diff[5] ),
        .gpr_6  (regs_diff[6] ),
        .gpr_7  (regs_diff[7] ),
        .gpr_8  (regs_diff[8] ),
        .gpr_9  (regs_diff[9] ),
        .gpr_10 (regs_diff[10]),
        .gpr_11 (regs_diff[11]),
        .gpr_12 (regs_diff[12]),
        .gpr_13 (regs_diff[13]),
        .gpr_14 (regs_diff[14]),
        .gpr_15 (regs_diff[15]),
        .gpr_16 (regs_diff[16]),
        .gpr_17 (regs_diff[17]),
        .gpr_18 (regs_diff[18]),
        .gpr_19 (regs_diff[19]),
        .gpr_20 (regs_diff[20]),
        .gpr_21 (regs_diff[21]),
        .gpr_22 (regs_diff[22]),
        .gpr_23 (regs_diff[23]),
        .gpr_24 (regs_diff[24]),
        .gpr_25 (regs_diff[25]),
        .gpr_26 (regs_diff[26]),
        .gpr_27 (regs_diff[27]),
        .gpr_28 (regs_diff[28]),
        .gpr_29 (regs_diff[29]),
        .gpr_30 (regs_diff[30]),
        .gpr_31 (regs_diff[31]) 
    );

    wire trap_valid_t = trap_valid[4] ;
    DifftestTrapEvent DifftestTrapEvent(
        .clock    (i_clk       ),
        .coreid   (0           ),
        .valid    (trap_valid_t),
        .code     (trap_code   ),
        .pc       (pc_t        ),
        .cycleCnt (cycleCnt    ),
        .instrCnt (instrCnt    ) 
    );

    wire [63:0] mstatus_t  = mstatus [255:192] ;
    wire [63:0] mepc_t     = mepc    [255:192] ;
    wire [63:0] mtvec_t    = mtvec   [255:192] ;
    wire [63:0] mcause_t   = mcause  [255:192] ;
    wire [63:0] mip_t      = mip     [255:192] ;
    wire [63:0] mie_t      = mie     [255:192] ;
    wire [63:0] mscratch_t = mscratch[255:192] ;
    wire [63:0] sstatus_t  = mstatus_t & 64'h8000_0003_000d_e122 ;
    DifftestCSRState DifftestCSRState(
        .clock          (i_clk     ),
        .coreid         (0         ),
        .priviledgeMode (3         ),
        .mstatus        (mstatus_t ),//
        .sstatus        (sstatus_t ),
        .mepc           (mepc_t    ),//
        .sepc           (0         ),
        .mtval          (0         ),
        .stval          (0         ),
        .mtvec          (mtvec_t   ),//
        .stvec          (0         ),
        .mcause         (mcause_t  ),//
        .scause         (0         ),
        .satp           (0         ),
        .mip            (mip_t     ),//
        .mie            (mie_t     ),//
        .mscratch       (mscratch_t),//
        .sscratch       (0         ),
        .mideleg        (0         ),
        .medeleg        (0         ) 
    );
    
    wire        trap_ecall_t = trap_ecall[4] ;
    wire        trap_timer_t = trap_timer[4] ;
    wire [63:0] intrNO       = trap_timer_t ? mcause_t : 64'd0 ;
    wire [63:0] cause        = trap_ecall_t ? mcause_t : 64'd0 ;
    DifftestArchEvent DifftestArchEvent(
        .clock         (i_clk  ),
        .coreid        (0      ),
        .intrNO        (intrNO ), 
        .cause         (cause  ),
        .exceptionPC   (pc_t   ),
        .exceptionInst (instr_t) 
    );
//----------------------------------------------------------------
endmodule
//----------------------------------------------------------------
